/*
 * Memory game, using solid tiles.
 *
 * This is a two player game, in which you try and match two tiles of the
 * same color. Each player takes alternate turns, turning over two tiles,
 * if they match, you get a point, and the tiles are removed from the board.
 * If they do not match, they are turned face-down again.
 *
 * Use Up/Down/Left/Right arrows to move the pointer, SPACE to turn a tile
 * over. To quit the game, press ESCAPE
 *
 * Contributed by Tom StDenis
 *
 * Compile command: cc memory -pof
 */
#include <lrg.h>
#include <stdio.h>

#define _KUA		0x4800		/* Up    arrow	*/
#define _KDA		0x5000		/* Down  arrow	*/
#define _KLA		0x4B00		/* Left  arrow	*/
#define _KRA		0x4D00		/* Right arrow	*/

extern unsigned RAND_SEED;

int tiles;			/* square root of number of tiles */

unsigned char tile_map[256];
/* holds actual tiles, 1-16 are the tile values (colors), 0 means it's been
removed, if the 8th bit (0x80) is set it means the tile is fliped (showing
it's color) */

int score[2];		/* player scores */
int tx[2], ty[2];	/* x and y of the two chosen tiles */
int mx, my;			/* the marker x/y */
char p;				/* current player */

int winning[] = {1000, 100, 1100, 100, 1200, 100, 1300, 100, 0};
int losing[]  = {500, 75, 450, 75, 400, 100, 350, 25,375, 25,350, 25,375, 25,0};

play_snd(int *a)
{
	while(*a != 0)
		beep(*a++, *a++);	/* This is OK since MC handles its args in order */
}

/* colors 1-16 are for the actual tiles (their fronts) 17 is their backs */
draw_screen()
{
   int vsize, hsize;	/* vertical and horizontal size */
   int a, b;

   vsize = (185 / tiles);
   hsize = (310 / tiles);

	/* draw tiles */
	lrg_retrace();
	for(a = 0; a < tiles; ++a)
		for(b = 0; b < tiles; ++b) {
			if(!tile_map[a*tiles+b])				/* draw a blank */
				lrg_fbox(b * hsize , a * vsize , hsize, vsize, 0);
			else if(tile_map[a*tiles+b] & 0x80)		/* tile is showing */
				lrg_fbox(b * hsize , a * vsize , hsize, vsize,
					tile_map[a*tiles+b] & 0x7F);
			else									/* tile not showing */
				lrg_fbox(b * hsize , a * vsize , hsize, vsize, 17);
			/* draw empty box around card */
			lrg_box(b * hsize, a * vsize, hsize, vsize, 18); }

   /* draw marker */
   lrg_fcircle(mx * hsize + (hsize / 2), my * vsize + (vsize / 2), vsize / 2, 19);

   /* draw scores */
   lrg_printf(0, 190, B_YELLOW << 8 | BLUE, "Player 1: %u", score[0]);
   lrg_printf(200, 190, B_WHITE << 8 | RED, "Player 2: %u", score[1]);
   lrg_printf(115, 190, B_WHITE << 8 | BLACK, p ? "Player 2" : "Player 1");
}

/* the actual game */
int game_loop()
{
	int c;		/* player number and card number */

	c = 0;
	for(;;) {
		draw_screen();
		switch(kbget()) {
			case _KUA:	my = my ? my-1 : (tiles-1);			break;
			case _KDA: 	my = (my < (tiles-1)) ? my+1 : 0;	break;
			case _KLA:	mx = mx ? mx-1 : (tiles-1);			break;
			case _KRA:	mx = (mx < (tiles-1)) ? mx+1 : 0;	break;
			case ' ':
				if(tile_map[my * tiles + mx]) {
					if(c++ == 0) {
						tile_map[my * tiles + mx] |= 0x80;
						tx[0] = mx; ty[0] = my; }
					else if((mx != tx[0]) || (my != ty[0])) {
						/* let's see if they matched */
						tile_map[my * tiles + mx] |= 0x80;
						draw_screen();
						delay(500);
						tx[1] = mx; ty[1] = my;

						/* are they the same? */
						if(tile_map[ty[0] * tiles + tx[0]]
						== tile_map[ty[1] * tiles + tx[1]]) {
							++score[p];
							tile_map[ty[0] * tiles + tx[0]] =
							tile_map[ty[1] * tiles + tx[1]] = 0;
							/* play nice winning sound? */
							play_snd(winning); }
						else {
							/* extra delay to see the tiles */
							delay(250);
							tile_map[ty[0] * tiles + tx[0]] ^= 0x80;
							tile_map[ty[1] * tiles + tx[1]] ^= 0x80;
							p = !p;

							/* play nice losing sound ? */
							play_snd(losing); }
						c = 0; } }
				break;
			case 0x1B: return 0; }
		if((score[0] + score[1]) == ((tiles*tiles)/2))
			return -1;	}
}


main(int argc, char **argv)
{
	int a, b, c, d;
	static char palette[4][3] = {		/* New palette color values */
		31, 16, 16,			/* Last card color (normally black) */
		48,  0,  0,			/* Back of cards */
		 0, 48, 16,			/* Border */
		 0, 48, 48 };		/* Marker */

	get_time(&a, &b, &c);
	RAND_SEED = (a*1800) + (b*800) + c;

	if(argc <= 1)
		abort("Syntax: memory square_root_of_num_of_tiles\nFor example: MEMORY 16\n");

	tiles = atoi(argv[1]);
	if((tiles < 2) || (tiles > 16))
		abort("Valid values for ammount of tiles is between 2 and 16");

	if(tiles & 0x01)
		abort("Odd number of tiles not supported.");

	if(lrg_open())
		abort("Vga required.");

	/* setup colors */
	for(a=0; a < (sizeof(palette)/sizeof(palette[0])); ++a)
		lrg_setpal(a+16, palette[a]);

	/* Generate the pseudo-random deck of tiles */
	d = (tiles * tiles);
	b = random(16)+1;
	/* Lay out the tiles, two at a time */
	for(a=0; a < d; a += 2)
		tile_map[a] = tile_map[a+1] = b = ((b+1)%16)+1;
	/* Shuffle the tiles */
	for(a=0; a < d; ++a) {
		/* For small even numbers, the pseudo-random number generator */
		/* shows patterns, give it a kick to keep it sort-of "random" */
		RAND_SEED += a;
		b = tile_map[a];
		tile_map[a] = tile_map[c = random(d)];
		tile_map[c] = b; }

	a = game_loop();

	lrg_close();

	if(!a)
		puts("No one wins, quitters!!!");
	else if(score[0] > score[1])
		puts("Player one wins!!!");
	else if(score[1] > score[0])
		puts("Player two wins!!!");
	else
		puts("We have a tie!!!");
}
